package pl.edu.pw.perg.compinfodb.datamodel;

import java.beans.PropertyChangeEvent;
import java.beans.PropertyChangeListener;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.Enumeration;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.List;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.TimeoutException;

import javax.swing.JTree;
import javax.swing.event.TreeSelectionEvent;
import javax.swing.event.TreeSelectionListener;
import javax.swing.tree.DefaultTreeModel;
import javax.swing.tree.MutableTreeNode;

import org.jdesktop.swingworker.SwingWorker;
import org.jdesktop.swingworker.SwingWorker.StateValue;
import org.springframework.richclient.progress.ProgressBarProgressMonitor;
import org.springframework.richclient.progress.ProgressMonitor;

import pl.edu.pw.perg.compinfodb.command.IActiveModelListener;
import pl.edu.pw.perg.compinfodb.entities.VirtualElement;
import pl.edu.pw.perg.compinfodb.manager.IVirtualElementManager;

@SuppressWarnings("serial")
public class StructureTreeModel extends DefaultTreeModel implements
		TreeSelectionListener, PropertyChangeListener, IActivableModel {

	ProgressMonitor pm;
	StructureTask task;
	private boolean clear = false;
	List<IActiveModelListener> activeModelListeners = new LinkedList<IActiveModelListener>();

	private final class StructureTask extends SwingWorker<String, String> {

		@Override
		protected String doInBackground() throws Exception {

			for (int i = 0; i < listeners.size(); i++) {
				int a = (i + 1) * 100 / listeners.size();
				setProgress(a);
				listeners.get(i).selectionChanged(selectedElement);
			}
			return null;
		}

		@Override
		protected void done() {
			pm.worked(100);
			super.done();
		}

	}

	// private IVirtualElementManager elementManager;
	private VirtualElement selectedElement;
	private List<IStructureTreeSelectionListener> listeners = new ArrayList<IStructureTreeSelectionListener>();

	public StructureTreeModel(IVirtualElementManager elementManager) {
		super(getStructureTree(elementManager));

	}

	private static SelectableTreeNode createTree(VirtualElement elt) {
		SelectableTreeNode rootNode = new SelectableTreeNode(elt);
		Collection<VirtualElement> children = elt.getChildren();
		for (Iterator<VirtualElement> i = children.iterator(); i.hasNext();) {
			VirtualElement child = (VirtualElement) i.next();
			rootNode.add(createTree(child));
		}
		return rootNode;

	}

	private static SelectableTreeNode getStructureTree(
			IVirtualElementManager elementManager) {
		SelectableTreeNode rootNode = new SelectableTreeNode("CMS Experiment");
		List<VirtualElement> elements = elementManager.findRoots();
		List<VirtualElement> notCables = new LinkedList<VirtualElement>();
		for (VirtualElement virtualElement : elements) {
			if (!virtualElement.getElementType().isCable())
				notCables.add(virtualElement);
		}
		Collections.sort(notCables);
		for (Iterator<VirtualElement> i = notCables.iterator(); i.hasNext();) {
			VirtualElement elt = (VirtualElement) i.next();
			SelectableTreeNode eltNode = createTree(elt);
			rootNode.add(eltNode);
		}
		VirtualElement spareVe = new VirtualElement("SPARE");
		SelectableTreeNode spares = new SelectableTreeNode(spareVe);
		rootNode.add(spares );
//		VirtualElement allVe = new VirtualElement("ALL");
//		SelectableTreeNode all = new SelectableTreeNode(allVe);
//		rootNode.add(all );
		return rootNode;
	}

	public void displayElementBranch(VirtualElement ve) {
		SelectableTreeNode node = (SelectableTreeNode) findNodeWithValue(
				(SelectableTreeNode) getRoot(), ve);
		for (Enumeration<SelectableTreeNode> e = node.getParent().children(); e
				.hasMoreElements();) {
			SelectableTreeNode child = e.nextElement();
			if (child.equals(node))
				continue;
			child.setVisible(false);
		}

	}

	private SelectableTreeNode findNodeWithValue(SelectableTreeNode root,
			Object value) {
		if (root.getUserObject() != null && root.getUserObject().equals(value))
			return root;
		SelectableTreeNode child = null;
		for (Enumeration<SelectableTreeNode> e = root.children(); e
				.hasMoreElements();) {
			child = e.nextElement();
			SelectableTreeNode found = findNodeWithValue(child, value);
			if (found != null)
				return found;
		}
		return null;
	}

	public VirtualElement getSelectedElement() {
		return selectedElement;
	}

	public void setSelectedElement(VirtualElement selectedElement) {
		this.selectedElement = selectedElement;
	}

	public void addSelectionListener(IStructureTreeSelectionListener l) {
		listeners.add(l);
	}

	public void valueChanged(TreeSelectionEvent e) {
		SelectableTreeNode selectedNode = (SelectableTreeNode) ((JTree) e
				.getSource()).getLastSelectedPathComponent();
		if (selectedNode == null)
			return;
		Object element = selectedNode.getUserObject();
		if (element instanceof VirtualElement)
			selectedElement = (VirtualElement) element;
		// for (IStructureTreeSelectionListener l : listeners) {
		// l.selectionChanged(selectedElement);
		// }
		pm.taskStarted("Loading...", 99);
		if (task == null || clear) {
			task = new StructureTask();
			task.addPropertyChangeListener(this);
			task.execute();
		}

//		 for (int i = 0; i < listeners.size(); i++) {
//		 int a = (i+1)*100/listeners.size();
//		 // setProgress(a);
//		 listeners.get(i).selectionChanged(selectedElement);
//		 }

	}

	public void propertyChange(PropertyChangeEvent evt) {
		// task = (StructureTask) evt.getSource();
		if (evt.getPropertyName().equals("progress"))
			pm.worked(task.getProgress());
		else {
			SwingWorker.StateValue state = (StateValue) evt.getNewValue();
			if (state.equals(SwingWorker.StateValue.DONE))
				clear = true;
			else
				clear = false;
		}
		// pm.worked(50);

	}

	public void setPm(ProgressMonitor pm) {
		this.pm = pm;
	}

	public void addActiveModelListener(IActiveModelListener activeModelListener) {
		activeModelListeners.add(activeModelListener);
		
	}

	public void setActive() {
		for (IActiveModelListener l : activeModelListeners) {
			l.setActiveModel(null);
		}
		
	}

}
